package com.ruyuan2020.furnishing.tender.service.impl;

import com.google.common.base.Objects;
import com.ruyuan2020.common.exception.BusinessException;
import com.ruyuan2020.furnishing.account.api.AccountApi;
import com.ruyuan2020.furnishing.account.domain.GoldOperationRequestDTO;
import com.ruyuan2020.furnishing.member.api.MemberApi;
import com.ruyuan2020.furnishing.tender.constant.TenderConstants;
import com.ruyuan2020.furnishing.tender.constant.TenderStatus;
import com.ruyuan2020.furnishing.tender.dao.BiddingDAO;
import com.ruyuan2020.furnishing.tender.dao.TenderDAO;
import com.ruyuan2020.furnishing.tender.domain.*;
import com.ruyuan2020.furnishing.tender.service.TenderService;
import com.ruyuan2020.furnishing.tender.service.state.TenderStateManager;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Optional;

@Service
public class TenderServiceImpl implements TenderService {

    @Autowired
    private TenderDAO tenderDAO;

    @Autowired
    private BiddingDAO biddingDAO;

    /**
     * 会员账户服务
     */
    @DubboReference(version = "1.0.0", cluster = "failfast")
    private AccountApi accountApi;

    @DubboReference(version = "1.0.0")
    private MemberApi memberApi;

    @Autowired
    private TenderStateManager tenderStateManager;

    /**
     * 创建招标信息
     *
     * @param tenderDTO 招标信息
     */
    @Override
    @Transactional
    public Long create(TenderDTO tenderDTO) {
        TenderDO tenderDO = tenderDTO.clone(TenderDO.class);
        tenderDO.setStatus(TenderStatus.WAITING_TENDER);
        // 没有实现配置系统
        // 设置投标费用
        tenderDO.setCostAmount(TenderConstants.DEFAULT_COST);
        // 设置最大投标数
        tenderDO.setBiddingMaxCount(TenderConstants.DEFAULT_BIDDING_MAX_COUNT);
        return tenderDAO.save(tenderDO);
    }

    /**
     * 投标
     *
     * @param biddingDTO 投标信息
     */
    @Override
    @Transactional
    public Long bid(BiddingDTO biddingDTO) {
        Integer count = biddingDAO.countBidding(biddingDTO.getTenderId(), biddingDTO.getMemberId());
        if (count > 0) {
            throw new BusinessException("您已经投过标了，不需要重复投标");
        }
        // 获取招标信息
        Optional<TenderDO> optional = tenderDAO.getBiddingInfoById(biddingDTO.getTenderId());
        TenderDO tenderDO = optional.orElseThrow(() -> new BusinessException("招标信息不存在"));
        TenderDTO tenderDTO = tenderDO.clone(TenderDTO.class);
        if (!tenderStateManager.canBid(tenderDTO)) {
            throw new BusinessException("不能执行投标操作");
        }
        // 使用金币支付投标费用
        payGoldForCost(biddingDTO.getMemberId(), tenderDO.getCostAmount());
        // 投标
        tenderStateManager.bid(tenderDTO);

        // 保存投标信息
        Long biddingId = saveBidding(biddingDTO, tenderDTO.getMemberId());
        // 通知会员系统，投标事件完成
        memberApi.informBidCompletedEvent(biddingDTO.getMemberId());
        return biddingId;
    }

    /**
     * 招标人签约
     *
     * @param signingDTO 签约信息
     */
    @Override
    public void sign(SigningDTO signingDTO) {
        BiddingDO biddingDO = biddingDAO.getById(signingDTO.getBiddingId()).orElseThrow(() -> new BusinessException("招标信息不存在"));
        TenderDO tenderDO = tenderDAO.getSigningInfoById(biddingDO.getTenderId()).orElseThrow(() -> new BusinessException("招标信息不存在"));
        if (!Objects.equal(tenderDO.getMemberId(), signingDTO.getMemberId())) {
            throw new BusinessException("招标信息不是您提交的，请查证");
        }
        TenderDTO tenderDTO = tenderDO.clone(TenderDTO.class);
        if (!tenderStateManager.canSign(tenderDTO)) {
            throw new BusinessException("不能执行签约操作");
        }
        // 签约
        BiddingDTO biddingDTO = new BiddingDTO();
        biddingDTO.setId(signingDTO.getBiddingId());
        biddingDTO.setBidderId(biddingDO.getBidderId());
        tenderStateManager.sign(tenderDTO);

        // 设置投标信息为签约
        biddingDAO.updateSigned(biddingDTO.getId());
        // 通知会员系统，签约事件完成
        memberApi.informSignedEvent(biddingDTO.getBidderId());
    }

    /**
     * 设置完工
     *
     * @param completionDTO 完工信息
     */
    @Override
    public void complete(CompletionDTO completionDTO) {
        Optional<TenderDO> tenderOptional = tenderDAO.getCompletionInfoById(completionDTO.getTenderId());
        TenderDO tenderDO = tenderOptional.orElseThrow(() -> new BusinessException("招标信息不存在"));
        if (!Objects.equal(tenderDO.getMemberId(), completionDTO.getMemberId())) {
            throw new BusinessException("招标信息不是您提交的，请查证");
        }
        TenderDTO tenderDTO = tenderDO.clone(TenderDTO.class);
        if (!tenderStateManager.canComplete(tenderDTO)) {
            throw new BusinessException("不能执行完工操作");
        }
        // 招标工程完成
        tenderStateManager.complete(tenderDTO);
    }


    /**
     * 检查是否可以支付保证金
     *
     * @param checkTrustRequestDTO 保证金信息
     */
    @Override
    public void checkPayTrust(CheckTrustRequestDTO checkTrustRequestDTO) {
        // 获取招标信息
        Optional<TenderDO> optional = tenderDAO.getTrustInfoById(checkTrustRequestDTO.getTenderId());
        TenderDO tenderDO = optional.orElseThrow(() -> new BusinessException("招标信息不存在"));
        if (tenderDO.getPayFlag()) {
            throw new BusinessException("保证金已支付");
        }
        if (!Objects.equal(tenderDO.getMemberId(), checkTrustRequestDTO.getMemberId())) {
            throw new BusinessException("招标信息不是您提交的，请查证");
        }
        if (tenderDO.getTrustAmount().compareTo(checkTrustRequestDTO.getAmount()) != 0) {
            throw new BusinessException("支付金额不合法");
        }
    }

    @Override
    @Transactional
    public void informPayTrustCompletedEvent(Long tenderId) {
        tenderDAO.updatePay(tenderId);
    }

    @Override
    public TenderDTO get(Long id) {
        return tenderDAO.getById(id).map(it -> it.clone(TenderDTO.class)).orElseThrow(() -> new BusinessException("招标信息不存在"));
    }

    /**
     * 使用金币支付投标费用
     *
     * @param bidderId 投标人会员id
     * @param cost     投标费用
     */
    private void payGoldForCost(Long bidderId, BigDecimal cost) {
        GoldOperationRequestDTO goldOperationRequestDTO = new GoldOperationRequestDTO();
        goldOperationRequestDTO.setMemberId(bidderId);
        goldOperationRequestDTO.setNumber(cost);
        goldOperationRequestDTO.setLog("投标");
        accountApi.payGold(goldOperationRequestDTO);
    }

    private Long saveBidding(BiddingDTO biddingDTO, Long tendererId) {
        BiddingDO biddingDO = new BiddingDO();
        biddingDO.setTenderId(biddingDTO.getTenderId());
        biddingDO.setBidderId(biddingDTO.getMemberId());
        biddingDO.setContent(biddingDTO.getContent());
        biddingDO.setTendererId(tendererId);
        return biddingDAO.save(biddingDO);
    }
}
